﻿// Copyright (c) 2012 Mokah Nnamezie & Andrew Cooper
// Neo4Net graph database - http://neo4net.codeplex.com
//
// This file is part of Neo4Net - a .Net port of the Neo4j graph database
//
// Neo4Net is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

namespace Neo4Net.Kernel.Impl
{
    using System;
    using System.IO;
    using System.Reflection;
    using Neo4Net.GraphDb;
    using Neo4Net.Kernel;
    using Neo4Net.Kernel.Impl.Core;
    using Neo4Net.Kernel.Impl.Nioneo.Store;
    using Neo4Net.Kernel.Impl.Transaction;
    using Neo4Net.Kernel.Impl.Util;
    using Neo4Net.Test;
    using NUnit.Framework;

    [AbstractNeo4NetTestCase.RequiresPersistentGraphDatabase(false)]
    partial class AbstractNeo4NetTestCase
    {
        protected static readonly DirectoryInfo neo4NetBaseDirectory = new DirectoryInfo(Path.Combine("target", "var"));

        private static IGraphDatabaseAPI graphDb;
        private Neo4Net.GraphDb.ITransaction tx;

        private static bool requiresPersistentGraphDatabase = false;

        public IGraphDatabaseService GraphDb
        {
            get { return graphDb; }
        }

        private static void SetupGraphDatabase(bool requiresPersistentGraphDatabase)
        {
            AbstractNeo4NetTestCase.requiresPersistentGraphDatabase = requiresPersistentGraphDatabase;
            if (requiresPersistentGraphDatabase)
            {
                try
                {
                    FileUtils.DeleteRecursively(new FileInfo(GetStorePath("neo-test")));
                }
                catch (IOException e)
                {
                    throw new SystemException(String.Empty, e);
                }
            }

            graphDb = (IGraphDatabaseAPI)(requiresPersistentGraphDatabase ?
                                          new TestGraphDatabaseFactory().NewEmbeddedDatabase(GetStorePath("neo-test")) :
                                          new TestGraphDatabaseFactory().NewImpermanentDatabase());
        }

        public IGraphDatabaseAPI EmbeddedGraphDb
        {
            get { return graphDb; }
        }

        protected virtual bool RestartGraphDbBetweenTests
        {
            get { return false; }
        }

        public Neo4Net.GraphDb.ITransaction Transaction
        {
            get { return tx; }
            set { tx = value; }
        }

        public static String GetStorePath(String endPath)
        {
            return new FileInfo(Path.Combine(neo4NetBaseDirectory.FullName, endPath)).FullName;
        }

        [SetUp]
        public void SetUpTest()
        {
            if (RestartGraphDbBetweenTests && graphDb == null)
            {
                SetupGraphDatabase(requiresPersistentGraphDatabase);
            }
            tx = graphDb.BeginTx();
        }

        [TearDown]
        public void TearDownTest()
        {
            if (tx != null)
            {
                tx.Finish();
            }

            if (RestartGraphDbBetweenTests)
            {
                TearDownDb();
            }
        }

        [TestFixtureTearDown]
        public static void TearDownDb()
        {
            try
            {
                if (graphDb != null) graphDb.Shutdown();
            }
            finally
            {
                graphDb = null;
            }
        }

        public Neo4Net.GraphDb.ITransaction NewTransaction()
        {
            if (tx != null)
            {
                tx.Success();
                tx.Finish();
            }
            tx = graphDb.BeginTx();
            return tx;
        }

        public void Commit()
        {
            if (tx != null)
            {
                tx.Success();
                tx.Finish();
                tx = null;
            }
        }

        public void Rollback()
        {
            if (tx != null)
            {
                tx.Failure();
                tx.Finish();
                tx = null;
            }
        }

        public NodeManager NodeManager
        {
            get { return graphDb.NodeManager; }
        }

        public static void DeleteFileOrDirectory(String path)
        {
            DeleteFileOrDirectory(new FileInfo(path));
            DeleteFileOrDirectory(new DirectoryInfo(path));
        }

        public static void DeleteFileOrDirectory(FileInfo file)
        {
            if (!file.Exists)
            {
                return;
            }

            file.Delete();
        }

        public static void DeleteFileOrDirectory(DirectoryInfo folder)
        {
            if (!folder.Exists)
            {
                return;
            }

            foreach (var child in folder.EnumerateDirectories())
            {
                DeleteFileOrDirectory(child);
            }

            foreach (var child in folder.EnumerateFiles())
            {
                DeleteFileOrDirectory(child);
            }

            folder.Delete();
        }

        protected void ClearCache()
        {
            EmbeddedGraphDb.NodeManager.ClearCache();
        }

        protected long PropertyRecordsInUse()
        {
            return GetPropertyStore().GetNumberOfIdsInUse();
        }

        protected long DynamicStringRecordsInUse()
        {
            return DynamicRecordsInUse("stringPropertyStore");
        }

        protected long DynamicArrayRecordsInUse()
        {
            return DynamicRecordsInUse("arrayPropertyStore");
        }

        private long DynamicRecordsInUse(string fieldName)
        {
            try
            {
                FieldInfo storeField = typeof(PropertyStore).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
                // TODO: AJC - Check if this line of java is needed
                //storeField.setAccessible(true);  
                return ((AbstractDynamicStore)storeField.GetValue(GetPropertyStore())).GetNumberOfIdsInUse();
            }
            catch (Exception e)
            {
                throw new SystemException(String.Empty, e);
            }
        }

        protected PropertyStore GetPropertyStore()
        {
            XaDataSourceManager dsMgr = graphDb.XaDataSourceManager;
            return dsMgr.GetNeoStoreDataSource().GetXaConnection().PropertyStore;
        }

    }
}
